import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from scipy.stats import pearsonr, kendalltau
import matplotlib.pyplot as plt
from typing import List, Tuple, Dict


def is_numeric_series(series: pd.Series) -> bool:
    """检查Series是否为数值类型"""
    try:
        pd.to_numeric(series, errors='raise')
        return True
    except (ValueError, TypeError):
        return False


def process_complex_column(series: pd.Series) -> pd.Series:
    """处理复数类型的列，提取模作为特征"""
    try:
        # 移除括号并转换为复数
        complex_series = series.str.replace('[()]', '', regex=True).apply(complex)
        return complex_series.apply(np.abs)  # 返回复数的模
    except:
        return series  # 如果处理失败，返回原始系列


def calculate_time_correlation(df: pd.DataFrame, time_col: str) -> pd.Series:
    """计算每个特征与时间的相关性（Pearson相关系数）"""
    time_values = df[time_col].values
    correlations = {}

    for col in df.columns:
        if col == time_col:
            continue

        # 检查是否为数值列
        if not is_numeric_series(df[col]):
            print(f"列 {col} 不是数值类型，已排除在时间相关性计算外")
            correlations[col] = 0
            continue

        # 处理可能的复数列
        if df[col].dtype == 'object' and df[col].str.contains(r'\(.*?\)').any():
            processed_col = process_complex_column(df[col])
            values = processed_col.values
        else:
            values = df[col].values

        try:
            # 检查是否为常量列
            if np.allclose(values, values[0]):
                correlations[col] = 0
            else:
                corr, _ = pearsonr(values, time_values)
                correlations[col] = abs(corr)  # 使用绝对值表示相关强度
        except Exception as e:
            print(f"计算列 {col} 的时间相关性时出错: {e}")
            correlations[col] = 0

    return pd.Series(correlations, name='time_correlation')


def calculate_monotonicity(df: pd.DataFrame, time_col: str) -> pd.Series:
    """计算每个特征的单调性（Kendall Tau系数）"""
    time_values = df[time_col].values
    monotonicity = {}

    for col in df.columns:
        if col == time_col:
            continue

        # 检查是否为数值列
        if not is_numeric_series(df[col]):
            print(f"列 {col} 不是数值类型，已排除在单调性计算外")
            monotonicity[col] = 0
            continue

        # 处理可能的复数列
        if df[col].dtype == 'object' and df[col].str.contains(r'\(.*?\)').any():
            processed_col = process_complex_column(df[col])
            values = processed_col.values
        else:
            values = df[col].values

        try:
            # 检查是否为常量列
            if np.allclose(values, values[0]):
                monotonicity[col] = 0
            else:
                tau, _ = kendalltau(values, time_values)
                monotonicity[col] = abs(tau)  # 使用绝对值表示单调程度
        except Exception as e:
            print(f"计算列 {col} 的单调性时出错: {e}")
            monotonicity[col] = 0

    return pd.Series(monotonicity, name='monotonicity')


def feature_selection_2d(df: pd.DataFrame, time_col: str,
                         weights: Dict[str, float] = None) -> pd.DataFrame:
    """
    基于时间相关性和单调性两个维度进行特征选择

    参数:
    df: 包含特征和标签的DataFrame
    time_col: 时间列的名称
    weights: 各维度权重，默认为等权重

    返回:
    包含每个特征在两个维度上得分和综合得分的DataFrame
    """
    # 计算各维度得分
    time_scores = calculate_time_correlation(df, time_col)
    mono_scores = calculate_monotonicity(df, time_col)

    # 合并得分
    common_features = list(set(time_scores.index) & set(mono_scores.index))
    scores = pd.concat([time_scores[common_features], mono_scores[common_features]], axis=1)

    # 设置默认权重
    if weights is None:
        weights = {'time_correlation': 0.5, 'monotonicity': 0.5}

    # 标准化各维度得分
    for col in scores.columns:
        scores[col] = (scores[col] - scores[col].min()) / (scores[col].max() - scores[col].min() + 1e-8)

    # 计算综合得分
    scores['composite_score'] = (
            scores['time_correlation'] * weights['time_correlation'] +
            scores['monotonicity'] * weights['monotonicity']
    )

    # 按综合得分排序
    scores = scores.sort_values('composite_score', ascending=False)

    return scores


def plot_feature_ranking(scores: pd.DataFrame, top_n: int = 10, save_path: str = None):
    """可视化特征排名并可选择保存图像"""
    # 设置中文字体
    plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'WenQuanYi Micro Hei', 'Heiti TC']
    plt.rcParams['axes.unicode_minus'] = False

    plt.figure(figsize=(12, 8))

    # 取前top_n个特征
    top_features = scores.head(top_n)

    # 绘制柱状图
    ax = top_features[['time_correlation', 'monotonicity', 'composite_score']].plot(
        kind='bar', figsize=(12, 8))
    plt.title('特征在各维度的得分及综合排名', fontsize=14)
    plt.xlabel('特征', fontsize=12)
    plt.ylabel('标准化得分', fontsize=12)
    plt.xticks(rotation=45, ha='right')
    plt.tight_layout()

    # 保存图像
    if save_path:
        plt.savefig(save_path, dpi=300, bbox_inches='tight')
        print(f"特征排名图已保存至: {save_path}")

    return plt


df = pd.read_excel('./水平.xlsx')

# 执行特征选择（移除了class_col参数）
scores = feature_selection_2d(df, time_col='文件编号')

# 保存结果到CSV文件
csv_path = './feature_selection_results.csv'
scores.to_csv(csv_path, encoding='utf-8-sig')
print(f"特征选择结果已保存至: {csv_path}")

# 输出排名前10的特征
print("\n排名前10的特征:")
print(scores.head(10))